#include "JSONUtil.h"
#include <vector>
#include <iosfwd>

simframework::json::JSONUtil::JSONUtil(void)
{
}

simframework::json::JSONUtil::~JSONUtil(void)
{
}

SimWString simframework::json::JSONUtil::getKey(SimWStringIter& startIt, SimWStringIter& endIt)
{
	removeSpaces(startIt, endIt);
	startIt++;

	SimWString key;
	for (; startIt != endIt; ++startIt)
	{
		if (*startIt != '"')
			key += *startIt;
		else
		{
			startIt++;
			break;
		}
	}
	return key;
}
void simframework::json::JSONUtil::skipValue(SimWStringIter& startIt, SimWStringIter& endIt)
{
	removeSpaces(startIt, endIt);
	if (*startIt != ':')
	{
		std::cout << "skipValue error : no colon\n";
		return;
	}
	startIt++; // skip :
	removeSpaces(startIt, endIt);

	switch(*startIt)
	{
	case '{':
		return JSONUtil::skipObjectOrArray(startIt, endIt);
		break;
	case '[':
		return JSONUtil::skipObjectOrArray(startIt, endIt);
		break;
	case '"':
		return JSONUtil::skipString(startIt, endIt);
		break;
	default:
		break;
	}
}

void simframework::json::JSONUtil::skipString(SimWStringIter& startIt, SimWStringIter& endIt)
{
	removeSpaces(startIt, endIt);

	if (*startIt != '"')
	{
		std::cout << "skipString error : no quotation\n";
		return;
	}

	startIt++; //skip "
	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '"')
		{
			startIt++;
			break;
		}
	}
}
void simframework::json::JSONUtil::skipNumber(SimWStringIter& startIt, SimWStringIter& endIt)
{
	removeSpaces(startIt, endIt);

	if (!(*startIt == '-' || isDigit(*startIt)))
	{
		std::cout << "skipNumber error : no digit or - at the start point\n";
		return;
	}

	for (; startIt != endIt; ++startIt)
	{
		if (!isDigit(*startIt))
		{
			break;
		}
	}
}

void simframework::json::JSONUtil::skipBoolean(SimWStringIter& startIt, SimWStringIter& endIt)
{
	removeSpaces(startIt, endIt);

	if (*startIt == 't')
	{
		SimWString s = L"true";
		(void)checkPatternString(s, startIt, endIt);
	}
	else if (*startIt == 'f')
	{
		SimWString s = L"false";
		(void)checkPatternString(s, startIt, endIt);
	}
	else
	{
		std::cout << "skipBoolean error : no T or F at the start point\n";
	}
}

void simframework::json::JSONUtil::skipObjectOrArray(SimWStringIter& startIt, SimWStringIter& endIt)
{
	std::vector<JSON_Type> stack;
	removeSpaces(startIt, endIt);

	switch (*startIt)
	{
	case '{':
		stack.push_back(JSON_OBJECT);
		break;
	case '[':
		stack.push_back(JSON_ARRAY);
		break;
	}
	startIt++; // skip { or [
	removeSpaces(startIt, endIt);

	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '{') stack.push_back(JSON_OBJECT);
		else if (*startIt == '[') stack.push_back(JSON_ARRAY);
		else if (*startIt == '}') stack.pop_back(); // need to check top
		else if (*startIt == ']') stack.pop_back(); // need to check top

		if (stack.empty())
		{
			startIt++;
			break;
		}
	}
}
void simframework::json::JSONUtil::removeSpaces(SimWStringIter& startIt, SimWStringIter& endIt)
{
	while (startIt != endIt && ::isspace(*startIt))
	{
		startIt++;
	}
}
void simframework::json::JSONUtil::cleanUp(SimWStringIter& startIt, SimWStringIter& endIt)
{
	removeSpaces(startIt, endIt);
	if (startIt != endIt && *startIt == ',')
	{
		startIt++;
		return;
	}
}
simframework::json::JSON_Object* simframework::json::JSONUtil::getObjectValue(SimWStringIter& startIt, SimWStringIter& endIt, int* error)
{
	std::vector<JSON_Type> stack;
	removeSpaces(startIt, endIt);

	if (*startIt != '{')
	{
		std::cout << "getObjectValue Error : no {\n";
		return NULL;
	}

	SimWString ws;
	bool valueFound = false;

	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '{') stack.push_back(JSON_OBJECT);
		else if (*startIt == '[') stack.push_back(JSON_ARRAY);
		else if (*startIt == '}') stack.pop_back(); // need to check top
		else if (*startIt == ']') stack.pop_back(); // need to check top
		wchar_t wc = *startIt;
		ws += wc;

		if (stack.empty())
		{
			valueFound = true;
			startIt++; // skip }
			break;
		}
	}
	if (valueFound == true)
	{
		return new JSON_Object(ws);
	}
	return NULL;
}

simframework::json::JSON_Array* simframework::json::JSONUtil::getArrayValue(SimWStringIter& startIt, SimWStringIter& endIt, int* error)
{
	std::vector<JSON_Type> stack;
	removeSpaces(startIt, endIt);

	if (*startIt != '[')
	{
		std::cout << "getObjectValue Error : no [\n";
		return NULL;
	}

	SimWString ws;
	bool valueFound = false;

	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '{') stack.push_back(JSON_OBJECT);
		else if (*startIt == '[') stack.push_back(JSON_ARRAY);
		else if (*startIt == '}') stack.pop_back(); // need to check top
		else if (*startIt == ']') stack.pop_back(); // need to check top
		wchar_t wc = *startIt;
		ws += wc;

		if (stack.empty())
		{
			valueFound = true;
			startIt++; // skip ]
			break;
		}
	}
	if (valueFound == true)
	{
		return new JSON_Array(ws);
	}
	return NULL;
}
simframework::json::JSON_String* simframework::json::JSONUtil::getStringValue(SimWStringIter& startIt, SimWStringIter& endIt, int* error)
{
	removeSpaces(startIt, endIt);

	SimWString ws;
	startIt++; // skip "
	bool valueFound = false;

	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '"')
		{
			valueFound = true;
			break;
		}
		if (*startIt == '\\')
		{
			startIt++;
			if (startIt == endIt) return NULL;
			if (*startIt == 'u')
			{
				startIt++;
				std::wstring hexValue;
				for (int i=0; (i < 4 && startIt != endIt); ++i, ++startIt)
				{
					hexValue += *startIt;
				}
				wchar_t* mbuf[2];
				memcpy((char *)mbuf, hexValue.c_str(), 4);
				for (int i=0; i < 2; ++i) {
					ws += mbuf[i];
				}
			}
			wchar_t wc = *startIt;
			ws += wc;
		}
		else
		{
			wchar_t wc = *startIt;
			ws += wc;
		}
	}
	if (valueFound == true)
		startIt++;

	JSON_String* data = new JSON_String;
	data->strValue = ws;
	return data;
}
simframework::json::JSON_Float* simframework::json::JSONUtil::getFloatValue(SimWStringIter& startIt, SimWStringIter& endIt, int* error)
{
	removeSpaces(startIt, endIt);
	SimWString ws;
	bool valueFound = false;

	if (startIt != endIt && *startIt == '-')
	{
		wchar_t wc = *startIt;
		ws += wc;
	}

	for(; startIt != endIt; ++startIt)
	{
		if (isDigit(*startIt))
		{
			wchar_t wc = *startIt;
			ws += wc;
			valueFound = true;
		}
		else
		{
			break;
		}
	}
	if (valueFound == true)
	{
		JSON_Float* data = new JSON_Float;
		data->fValue = atof((char *)ws.c_str());
		return data;
	}
	return NULL;
}
simframework::json::JSON_Integer* simframework::json::JSONUtil::getIntValue(SimWStringIter& startIt, SimWStringIter& endIt, int* error)
{
	removeSpaces(startIt, endIt);
	SimWString ws;
	bool valueFound = false;

	if (startIt != endIt && *startIt == '-')
	{
		wchar_t wc = *startIt;
		ws += wc;
	}

	for(; startIt != endIt; ++startIt)
	{
		if (isDigit(*startIt))
		{
			wchar_t wc = *startIt;
			ws += wc;
			valueFound = true;
		}
		else
		{
			break;
		}
	}
	if (valueFound == true)
	{
		JSON_Integer* data = new JSON_Integer;
		data->iValue = atoi((char *)ws.c_str());
		return data;
	}
	return NULL;
}
simframework::json::JSON_Boolean* simframework::json::JSONUtil::getBooleanValue(SimWStringIter& startIt, SimWStringIter& endIt, int* error)
{
	if (*startIt == 't') 
	{
		SimWString boolStr = L"true";
		bool res = checkPatternString(boolStr, startIt, endIt);
		if (res) {
			JSON_Boolean* data = new JSON_Boolean;
			data->bValue = true;
			return data;
		}
	}
	else if (*startIt == 'f')
	{
		SimWString boolStr = L"false";
		bool res = checkPatternString(boolStr, startIt, endIt);
		if (res) {
			JSON_Boolean* data = new JSON_Boolean;
			data->bValue = false;
			return data;
		}
	}
	return NULL;
}
bool simframework::json::JSONUtil::isDigit(wchar_t& c)
{
	switch(c)
	{
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
		{
			return true;
		}
	}
	return false;
}
bool simframework::json::JSONUtil::checkPatternString(SimWString& pattern, SimWStringIter& startIt, SimWStringIter& endIt)
{
	SimWStringIter itTmp = pattern.begin();
	SimWStringIter itTmpEnd = pattern.end();
	SimWStringIter it = startIt;

	bool found = true;
	unsigned int counter = 0;
	while (itTmp != itTmpEnd && it != endIt)
	{
		if (*it != *itTmp)
		{
			found = false;
		}
		itTmp++;
		it++;
		counter++;
	}
	if (found && counter == pattern.size())
	{
		// advance the original iterator
		for (unsigned int i=0; i < pattern.size(); ++i) startIt++;
		return true;
	}
	return false;
}
simframework::json::JSON_Object* simframework::json::JSONUtil::getObjectElementById(unsigned int elemId, unsigned int& start,
		SimWStringIter& startIt, SimWStringIter& endIt)
{
	for (unsigned int i=start; i < elemId; ++i,++start)
	{
		skipObjectOrArray(startIt, endIt);
		cleanUp(startIt, endIt);
	}
	return getObjectValue(startIt, endIt);
}
simframework::json::JSON_Array* simframework::json::JSONUtil::getArrayElementById(unsigned int elemId, unsigned int& start, 
		SimWStringIter& startIt, SimWStringIter& endIt)
{
	for (unsigned int i=start; i < elemId; ++i,++start)
	{
		skipObjectOrArray(startIt, endIt);
		cleanUp(startIt, endIt);
	}
	return getArrayValue(startIt, endIt);
}
simframework::json::JSON_String* simframework::json::JSONUtil::getStringElementById(unsigned int elemId, unsigned int& start, 
		SimWStringIter& startIt, SimWStringIter& endIt)
{
	for (unsigned int i=start; i < elemId; ++i,++start)
	{
		skipString(startIt, endIt);
		cleanUp(startIt, endIt);
	}
	return getStringValue(startIt, endIt);
}
simframework::json::JSON_Float* simframework::json::JSONUtil::getFloatElementById(unsigned int elemId, unsigned int& start, 
		SimWStringIter& startIt, SimWStringIter& endIt)
{
	for (unsigned int i=start; i < elemId; ++i,++start)
	{
		skipNumber(startIt, endIt);
		cleanUp(startIt, endIt);
	}
	return getFloatValue(startIt, endIt);
}
simframework::json::JSON_Integer* simframework::json::JSONUtil::getIntElementById(unsigned int elemId, unsigned int& start, 
		SimWStringIter& startIt, SimWStringIter& endIt)
{
	for (unsigned int i=start; i < elemId; ++i,++start)
	{
		skipNumber(startIt, endIt);
		cleanUp(startIt, endIt);
	}
	return getIntValue(startIt, endIt);
}
simframework::json::JSON_Boolean* simframework::json::JSONUtil::getBooleanElementById(unsigned int elemId, unsigned int& start, 
		SimWStringIter& startIt, SimWStringIter& endIt)
{
	for (unsigned int i=start; i < elemId; ++i,++start)
	{
		skipBoolean(startIt, endIt);
		cleanUp(startIt, endIt);
	}
	return getBooleanValue(startIt, endIt);
}

simframework::json::JSON_Abstract* simframework::json::JSONUtil::getDefault(JSON_Type& type)
{
	switch(type)
	{
	case JSON_OBJECT:	return &DEFAULT_JSON_OBJECT;
	case JSON_ARRAY:	return &DEFAULT_JSON_ARRAY;
	case JSON_STRING:	return &DEFAULT_JSON_STRING;
	case JSON_FLOAT:	return &DEFAULT_JSON_FLOAT;
	case JSON_INTEGER:	return &DEFAULT_JSON_INTEGER;
	case JSON_BOOLEAN:	return &DEFAULT_JSON_BOOLEAN;
	}
	return &DEFAULT_JSON_ABSTRACT; // should not reach here
}
